In my previous post I explored bitcoin data from different exchagnes, we also covered some arbitrage-related data. In part 2 of this series I will explore alt coin realted data.

R Libraries

Below is a list of R libraries we will be using to help us with our analysis. Not all of them are necessary but they all will make our life easier.

library(PoloniexR)
library(data.table)
library(lubridate)
library(Quandl)
library(plyr)
library(stringr)
library(ggplot2)
library(plotly)
library(janitor)
library(quantmod)
library(pryr)
library(corrplot)
library(PerformanceAnalytics)
library(tidyr)
library(MLmetrics)
library(tidyquant)
library(corrr)
library(cowplot)

Data

The best source I know off to get alt-coin data is through PoloniexR. I have written an R function to help download data.

get_alt_data <- function(tz = "UTC"
                         , coin = c("ETH", "LTC")
                         , add_bitcoin = TRUE
                         , return_in_USDT = TRUE
                         , from = "2017-01-01"
                         , to = "2018-04-09"
                         , period = "D"
                         , verbose = FALSE){
  
  # We will be using the public API
  poloniex.public <- PoloniexPublicAPI()
  
  # set the time zone to utc
  Sys.setenv(tz = tz)
  
  # convert from and to into time obj
  from  <- as.POSIXct(paste(from, tz, sep = ""))
  to    <- as.POSIXct(paste(to, tz, sep = ""))
  
  # lists to store data.tables and xts objects
  chart_list <- list()
  dt_list    <- list()
  
  # make sure the coin pair is in upper case
  coin       <- toupper(coin)
  coin_pairs <- paste0("BTC_", coin[coin != "BTC"])
  if(add_bitcoin | return_in_USDT) coin_pairs <- c("USDT_BTC", coin_pairs)
  
  # loop over the coins to get the data
  for(i in coin_pairs){
    if(verbose)
      invisible(cat('\tGetting data for ', i, ' pair\n'))
    
    # this is a list that will contain the chart data for each coin pair
    try(chart_list[[i]] <- ReturnChartData(theObject = poloniex.public
                                       , pair      = i
                                       , from      = from
                                       , to        = to
                                       , period    = period)
        , silent = TRUE)
    
    # list to contain data.tables 
    try(dt_list[[i]] <- as.data.table(chart_list[[i]]), silent = TRUE)
  }
  
  # convert to data.table and make sure to add a column containing the pairs
  coin_dt <- rbindlist(l = dt_list, use.names = TRUE, idcol = "pair")
  
  # return data in usdt prices
  if(return_in_USDT){
    # to get the price of the alt coin in usdt is not that simple but we'll do it
    # get a DT of the btc_usdt pair
    btc_usd <- coin_dt[pair == "USDT_BTC"]
    btc_usd <- btc_usd[, .(index, pair, weightedaverage)]
    setnames(btc_usd, c("Date", "USDT_BTC_pair", "USDT_BTC_price"))
    
    # get DT with only alt coins
    alt_coins <- copy(coin_dt)#[pair != "USDT_BTC"]
    
    # now we need to add an index to the alt_coins table, but first we have to rename the index column
    alt_coins[, Date := index]
    alt_coins[, index := 1:.N]
    setkey(alt_coins, index)
    
    # now merge the data tables
    coin_dt_usdt <- merge(x = alt_coins, y = btc_usd, by = "Date")
    
    # now calcualte the price in usdt
    coin_dt_usdt[, price_usdt := ifelse(pair == "USDT_BTC", USDT_BTC_price, weightedaverage * USDT_BTC_price)]
    
    # now get rid of the extra columns
    coin_dt_usdt[, c("USDT_BTC_price", "USDT_BTC_pair") := NULL]
    
    # we need to change some column names
    col_names_to_change <- c("pair", "high", "low", "open", "close", "volume", "quotevolume", "weightedaverage")
    col_names <- names(coin_dt_usdt)
    col_names[col_names %in% col_names_to_change] <- paste0(col_names_to_change, '_btc')
    
    setnames(coin_dt_usdt, col_names)
    
    # add a column for the usdt pair
    coin_dt_usdt[, pair_usdt := gsub("BTC_", "USDT_", pair_btc)]
    
    # adjust col order
    setcolorder(coin_dt_usdt, c(1:10, 12, 11))
    
    # set key again
    setkey(coin_dt_usdt, index)
    
    # now get rid of the index column since it is not needed anymore
    coin_dt_usdt[, index := NULL]
    
    # now put together the return list  
    return_list <- list(alt_chart_list = chart_list, alt_dt = coin_dt, alt_usdt_dt = coin_dt_usdt)
  }else{
    return_list <- list(alt_chart_list = chart_list, alt_dt = coin_dt)
  }
  
  return(return_list)
}

The function above can be used to download data for multiple coin at the same time. The function returns a data.table object with data for all coins in the function call. Even if the user doesn’t add bitcoin to the list of coins, the function adds bitcoin by default. This can be deactivated with the add_bitcoin argument. Here is an example

# get alt data for some coins
alt_data <- get_alt_data(return_in_USDT = T
                         , from = "2015-01-01"
                         , coin = c('ETH','XRP', 'BCH', 'LTC', 'NEO', 'XMR', 'DASH', 'XEM'))[['alt_usdt_dt']]

Let’s look at the data we just downloaded

head(alt_data)

The table shows the date, OHLC, Volume, and weightedaverage price in BTC. It also shows the pair and we added the price in USD.

Bitcoin-Altcoins Correlations

Wheneven I look at the prices of the coins available on my coinbase app I always get struck by the similarity of the price trends between the four coins available on coinbase: BTC, ETH, BCH, and LTC, see Figure below. So I thought it will be a good idea to explore the correlation in price trends between altcoins and bitcoin.

Apparent correlation between the prices of Bitcoin and other coins on coinbase.

Apparent correlation between the prices of Bitcoin and other coins on coinbase.

Let’s look at price trends of the coins we just downloaded. To better see potential correlations I am going to only zoon in on 2018.

p <- ggplot(alt_data[year(Date) == 2018], aes(x = Date, y =  price_usdt, col = pair_usdt)) + geom_line()
p <- p + facet_wrap(~pair_usdt, scales = "free", ncol = 3) + theme_minimal() + theme(legend.position="none") + ylab("Price (USD)")
p

The figure above shows that some coins seems to be more correlated with Bitcoin than others. The figure also shows that this variablity between Bitcoin and another coin varies over time. More on this below.

Tyring to find correlations bewteen time series data using Pearson correlation coefficient or other metrics used with stationary data, time series is not a form of stationary data, can give misleading results. Similar trends in time series data can also be very misleading, a nice article on this topic can be found here. And always remember that Correlation doesn’t guarantee Causation

Bottom line is the following, one has to be careful when cross-correlating time serice. In order to perform proper correlation analysis we need to add some new variables to our table.

Percentage Daily Change

Percentage daily change calculates the price change of a coin over a period of a day. Let’s add that to the table. Notice that we are calcualting this variable using the USD price, and not the price in Bitcoin.

# add daily price change
alt_data[, pct_change := Delt(price_usdt), by = pair_usdt]

Normalized Price in USD

Since the prices vary a lot, both overtime for the same coin and between coins, we will add a variable of the normalized price in USD. This variable will make it easy to plot prices of coins on the same figure.

# add normalized prices in udst
alt_data[, price_usdt_norm := price_usdt/max(price_usdt), by = pair_usdt]

Now that we have the normalized prices in USD, let’s look at the prices of bitcoin and litcoin on the same figure. We’ll do that for 2018 so we can better see any possible correlations.

p <- ggplot(alt_data[year(Date) == 2018 & pair_usdt %like% "BTC|LTC"], aes(x = Date, y =  price_usdt_norm, col = pair_usdt)) + geom_line()
p <- p + theme_minimal() + ylab("Price (USD)")
ggplotly(p)

preserved05fe9ce95c43077

The trends in the prices of BTC and LTC are very similar, Let’s look at price trends for 2017

p <- ggplot(alt_data[year(Date) == 2017 & pair_usdt %like% "BTC|LTC"], aes(x = Date, y =  price_usdt_norm, col = pair_usdt)) + geom_line()
p <- p + theme_minimal() + ylab("Price (USD)")
ggplotly(p)

preserveda06b461ab2353c0

Seems like we need to zoon in on the last quarter of 2017, let’s do that

p <- ggplot(alt_data[Date >= "2017-10-01"  & Date < "2018-01-01" &  pair_usdt %like% "BTC|LTC"], aes(x = Date, y =  price_usdt_norm, col = pair_usdt)) + geom_line()
p <- p + theme_minimal() + ylab("Price (USD)")
ggplotly(p)

preserve5abbaa60ea05b01f

It is clear from the above figure that the correlation in the prices of bitcoin and LTC vary over time. Note how the highest price for bitcoin on December 17 2017, preceeded that of LTC by two days, which occured on December 19 2017. This wasn’t the case for the ATH which occurred on January 6th 2018 for both coins.

Static Correlations (and why you shouldn’t use them with crypto!)

Up until now I haven’t calculated any correltaions between the price of different coins. You might ask why should we even care about correlations in time series. Well, in the case of financial time series data, if one can show that a correlation exists between two time series then one can use this correlation to model/predict the price movement of one coin/stock given the price trends of another coin/stock. However, as we mentioned earlier, correlation for time series data is not static, it changes over time. Actually let’s show that. To do that I am going to be calculating the Pearson correlation coefficient. In simple words, Pearson correlation coefficient for two vectors of data is a measure that shows how correlated these two vectors of data are. The value of this coefficient varies from -1, perfectly anti-correlated, to 1, perfectly correlated. So the correlation coefficient for a series of numbers on itself is 1. A value of zero means these is no correlation. Remember, this only works for static data.

In order to perform correlation on our data I am going to need to do some data transformation:

# subset data, only keep the date, the pair, and the price
alt_data_sub <- alt_data[, .(Date, pair_usdt, price_usdt)]
# convert to wide format 
alt_data_sub <- spread(data = alt_data_sub, key = "pair_usdt", value = "price_usdt")
# clean column names
setnames(alt_data_sub, gsub("USDT_", "", colnames(alt_data_sub)))

The new table we created contains the date along with the prices in USDT for each coin we have in our table.

tail(alt_data_sub)

Again, what I am doing here is not correct, I am just trying to show you why we shouldn’t be doing static correlations on crypto data. Now we’ll calculate the Pearson correlation coefficient between the coins we have, then we are going to make a nice plot of these coefficients.

# calculate the correlation matrix
M <- cor(alt_data_sub[, -1], use = "complete.obs") # notice how we are ignoring missing data with the last argument
# plot the correlation matrix
corrplot.mixed(corr = M, upper = "ellipse", lower = "number", order = "AOE", tl.col = "black")

The figure above shows the correlation coefficients between the different coins. It is easy to read, visually, the darker the color of the ellipse, and the more diagonal the ellipse, the higher the correlation coefficient. Of course you can also just look at the numbers on the bottom left part of the figure to get the value of the coefficient between two coins :). The figure shows how highly correlated the prices of crypto currencies can be. For example XRP and XEM have a correlation coefficient of 0.93. The highest correlation seems to be between BCH and DASH at 0.97 correlation coefficient.

All of the correlation coefficient we see in the above figure are significant, the question is, do these correlations vary over time. To answer this question I will calculate the correlation coefficient between Bitcoin and DASH on a monthly basis, you can do that for any time period, and will show that this coefficient varies greatly over time. Let’s do that

# subset the data
btc_dash <- alt_data_sub[, .(Date, BTC, DASH)]
# add a year_month column
btc_dash[, year_month := as.yearmon(Date)]
# calculate the correlation coefficient on montly basis
btc_dash_2 <- btc_dash[, cor(BTC, DASH), by = year_month]
# now plot the correlation coefficient as a function of month and year
plot(btc_dash_2$year_month, btc_dash_2$V1, xlab = "Year-Month", main = "Correlation Coeff. Between BTC and DASH Over time"
     , ylab = "Correlation Coefficient", type = "b", pch = 19, col = ifelse(btc_dash_2$V1 > 0, "blue", "red")
     , ylim = c(-1, 1))

This is interesting, the value of the monthly correlation coefficient between bitcoin and DASH varies between -0.91, highly anti-correlated, to 0.98, highly correlated. And this is why you should never use static correlation metrics with crypto data!

Correlation Networks

There is one more plot I would like to make, which is a network plot of the correlations between the different coins. The correlation network plot helps show strengths of correlation between the different coins. Agian, these correlations are time dependent and the figure we will be making will change over time, but I still think it is a good figure to make. Here it is:

# we will be using the great corrr package for this work
# get the correlation matrix, just like we did before
# buile the correlation matrix 
# the code snippets below are taken from 
# http://www.business-science.io/timeseries-analysis/2017/07/30/tidy-timeseries-analysis-pt-3.html
corr_2 <- correlate(alt_data_sub[, -1])
# make the network plot
# Network plot
corr_net <- corr_2 %>%
  network_plot(colours = c(palette_light()[[2]], "white", palette_light()[[4]]), legend = TRUE) +
  labs(
    title = "Static Correlations of some Crypto Currencies",
    subtitle = "2014 through 2018"
  ) +
  theme_tq() +
  theme(legend.position = "bottom")
corr_net

The figure above shows a network which measures how strongly correlated the prices of the coins under stugy are. The darker the color of the edge, line, connecting two coins and the closer they are in the network the stronger the correlation between these two coins.

From the figure, it seems like XMR, LTC, and BTC are in the heart of this network, while BCH seems to be the least correlated with the rest of the coins. Let’s see how the network plot changes between 2017 and 2018:

# subset the data and get correlation matrices
corr_2017 <- correlate(alt_data_sub[year(Date) == 2017][, -1])
corr_2018 <- correlate(alt_data_sub[year(Date) == 2018][, -1])
# build Network plots
corr_net_2017 <- corr_2017 %>%
  network_plot(colours = c(palette_light()[[2]], "white", palette_light()[[4]]), legend = TRUE) +
  labs(
    title = "Static Correlations of some Crypto Currencies",
    subtitle = "2017"
  ) +
  theme_tq() +
  theme(legend.position = "bottom")
corr_net_2018 <- corr_2018 %>%
  network_plot(colours = c(palette_light()[[2]], "white", palette_light()[[4]]), legend = TRUE) +
  labs(
    title = "Static Correlations of some Crypto Currencies",
    subtitle = "2018"
  ) +
  theme_tq() +
  theme(legend.position = "bottom")
# combine network plots
cow_net_plots <-plot_grid(corr_net_2017, corr_net_2018, ncol = 2)
title <- ggdraw() + 
    draw_label(label = 'Crypto Correlation Networks',
               fontface = 'bold', size = 18)
cow_out <- plot_grid(title, cow_net_plots, ncol=1, rel_heights=c(0.1, 1))
cow_out

As can be seen, the correlation networks do change overtime. This is not news since we already saw in the previous section that the value of the correlation varies overtime (I know we showed this to be true for the BTC-DASH air but we’ll show that this is true for the rest of the coins in the next section.)

Daily Returns Correlations

Let’s look at the percentage daily changes of the altcoins between 2015 and today.

# plot the percent changes
p <- ggplot(alt_data[Date > ymd("2015-01-01")], aes(x = Date, y =  (100*pct_change), col = pair_usdt)) + geom_line()
p <- p + ggtitle("% Daily Returns over time") + ylab("Daily Return (%)") 
p <- p + theme_bw() + guides(col=guide_legend(title="Coin Pair"))
ggplotly(p)

preservef70988aade66a9e2

Although the above figure is very cluttered, one thing is certain, percentage daily returns vary greatly for crypto. Let’s try to make this figure a bit easier to read

p <- ggplot(alt_data[Date > ymd("2015-01-01")], aes(x = Date, y =  (100*pct_change), col = pair_usdt)) + geom_line() + facet_wrap(~ pair_usdt)
p <- p + ggtitle("Percentage Daily Returns over time") + ylab("Daily Return (%)") 
p <- p + theme_bw() + theme(legend.position="none") 
ggplotly(p)

preserve8338a12cb835e739

It is kind of surprising that Bitcoin has the least variability in daily returns. The nice big spike around April 2nd 2017 shows a percentage daily return of ~88% for XRP, this is the highest daily return I have seen!

Let’s look at the percentage daily returns for Bitcoin and Litecoin since they seem to be highly correlated. I am going to zoom in on the time period 2016-02-01 and 2016-05-01.

start_date <- ymd("2016-02-01")
end_date <- ymd("2016-05-01")
p <- ggplot(alt_data[pair_usdt %like% "BTC|LTC" & Date > start_date & Date < end_date], aes(x = Date, y =  (100*pct_change), col = pair_usdt)) + geom_line() + theme_bw() + ylab("Price (USD)")
p

The figure shows clear correlation between the daily returns of Bitcoin and litcoin. It also shows that these correlations can vary overtime. In fact, let’s look at how these correlations vary overtime.

# these steps are similar to the ones in the previous section, the only differnect is that now we are looking at the percentage change in price difference on daily basis instead of the actual price
# subset data, only keep the date, the pair, and the price
alt_data_sub_pct <- alt_data[, .(Date, pair_usdt, pct_change)]
# convert to wide format 
alt_data_sub_pct <- spread(data = alt_data_sub_pct, key = "pair_usdt", value = "pct_change")
# clean column names
setnames(alt_data_sub_pct, gsub("USDT_", "", colnames(alt_data_sub)))
# subset the data
btc_ltc <- alt_data_sub_pct[, .(Date, BTC, LTC)]
# add a year_month column
btc_ltc[, year_month := as.yearmon(Date)]
# calculate the correlation coefficient on montly basis
btc_ltc_2 <- btc_dash[, cor(BTC, LTC), by = year_month]
Error in is.data.frame(y) : object 'LTC' not found

Rolling Correlations

LS0tCnRpdGxlOiAiQSBRdWljayBMb29rIGF0IENyeXB0byBDdXJyZW5jaWVzIHdpdGggUiAtIFBhcnQgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogIkRyLiBBb3VzIFwiQWxleFwiIEFiZG8gQGFvdXNhYmRvIgplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCkluIG15IHByZXZpb3VzIHBvc3QgSSBleHBsb3JlZCBiaXRjb2luIGRhdGEgZnJvbSBkaWZmZXJlbnQgZXhjaGFnbmVzLCB3ZSBhbHNvIGNvdmVyZWQgc29tZSBhcmJpdHJhZ2UtcmVsYXRlZCBkYXRhLiAKSW4gcGFydCAyIG9mIHRoaXMgc2VyaWVzIEkgd2lsbCBleHBsb3JlIGFsdCBjb2luIHJlYWx0ZWQgZGF0YS4gCgojIyBSIExpYnJhcmllcwpCZWxvdyBpcyBhIGxpc3Qgb2YgUiBsaWJyYXJpZXMgd2Ugd2lsbCBiZSB1c2luZyB0byBoZWxwIHVzIHdpdGggb3VyIGFuYWx5c2lzLiBOb3QgYWxsIG9mIHRoZW0gYXJlIG5lY2Vzc2FyeSBidXQgdGhleSBhbGwgd2lsbCBtYWtlIG91ciBsaWZlIGVhc2llci4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KFBvbG9uaWV4UikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShRdWFuZGwpCmxpYnJhcnkocGx5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkocXVhbnRtb2QpCmxpYnJhcnkocHJ5cikKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykKbGlicmFyeSh0aWR5cikKbGlicmFyeShNTG1ldHJpY3MpCmxpYnJhcnkodGlkeXF1YW50KQpsaWJyYXJ5KGNvcnJyKQpsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKIyMgRGF0YQpUaGUgYmVzdCBzb3VyY2UgSSBrbm93IG9mZiB0byBnZXQgYWx0LWNvaW4gZGF0YSBpcyB0aHJvdWdoIFtQb2xvbmlleFJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9Qb2xvbmlleFIvaW5kZXguaHRtbCkuIEkgaGF2ZSB3cml0dGVuIGFuIFIgZnVuY3Rpb24gdG8gaGVscCBkb3dubG9hZCBkYXRhLiAKCmBgYHtyIHBvbG9uaWV4X2Z1bmN0aW9ufQpnZXRfYWx0X2RhdGEgPC0gZnVuY3Rpb24odHogPSAiVVRDIgogICAgICAgICAgICAgICAgICAgICAgICAgLCBjb2luID0gYygiRVRIIiwgIkxUQyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGFkZF9iaXRjb2luID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgLCByZXR1cm5faW5fVVNEVCA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICwgZnJvbSA9ICIyMDE3LTAxLTAxIgogICAgICAgICAgICAgICAgICAgICAgICAgLCB0byA9ICIyMDE4LTA0LTA5IgogICAgICAgICAgICAgICAgICAgICAgICAgLCBwZXJpb2QgPSAiRCIKICAgICAgICAgICAgICAgICAgICAgICAgICwgdmVyYm9zZSA9IEZBTFNFKXsKICAKICAjIFdlIHdpbGwgYmUgdXNpbmcgdGhlIHB1YmxpYyBBUEkKICBwb2xvbmlleC5wdWJsaWMgPC0gUG9sb25pZXhQdWJsaWNBUEkoKQogIAogICMgc2V0IHRoZSB0aW1lIHpvbmUgdG8gdXRjCiAgU3lzLnNldGVudih0eiA9IHR6KQogIAogICMgY29udmVydCBmcm9tIGFuZCB0byBpbnRvIHRpbWUgb2JqCiAgZnJvbSAgPC0gYXMuUE9TSVhjdChwYXN0ZShmcm9tLCB0eiwgc2VwID0gIiIpKQogIHRvICAgIDwtIGFzLlBPU0lYY3QocGFzdGUodG8sIHR6LCBzZXAgPSAiIikpCiAgCiAgIyBsaXN0cyB0byBzdG9yZSBkYXRhLnRhYmxlcyBhbmQgeHRzIG9iamVjdHMKICBjaGFydF9saXN0IDwtIGxpc3QoKQogIGR0X2xpc3QgICAgPC0gbGlzdCgpCiAgCiAgIyBtYWtlIHN1cmUgdGhlIGNvaW4gcGFpciBpcyBpbiB1cHBlciBjYXNlCiAgY29pbiAgICAgICA8LSB0b3VwcGVyKGNvaW4pCiAgY29pbl9wYWlycyA8LSBwYXN0ZTAoIkJUQ18iLCBjb2luW2NvaW4gIT0gIkJUQyJdKQogIGlmKGFkZF9iaXRjb2luIHwgcmV0dXJuX2luX1VTRFQpIGNvaW5fcGFpcnMgPC0gYygiVVNEVF9CVEMiLCBjb2luX3BhaXJzKQogIAogICMgbG9vcCBvdmVyIHRoZSBjb2lucyB0byBnZXQgdGhlIGRhdGEKICBmb3IoaSBpbiBjb2luX3BhaXJzKXsKICAgIGlmKHZlcmJvc2UpCiAgICAgIGludmlzaWJsZShjYXQoJ1x0R2V0dGluZyBkYXRhIGZvciAnLCBpLCAnIHBhaXJcbicpKQogICAgCiAgICAjIHRoaXMgaXMgYSBsaXN0IHRoYXQgd2lsbCBjb250YWluIHRoZSBjaGFydCBkYXRhIGZvciBlYWNoIGNvaW4gcGFpcgogICAgdHJ5KGNoYXJ0X2xpc3RbW2ldXSA8LSBSZXR1cm5DaGFydERhdGEodGhlT2JqZWN0ID0gcG9sb25pZXgucHVibGljCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgcGFpciAgICAgID0gaQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIGZyb20gICAgICA9IGZyb20KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCB0byAgICAgICAgPSB0bwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHBlcmlvZCAgICA9IHBlcmlvZCkKICAgICAgICAsIHNpbGVudCA9IFRSVUUpCiAgICAKICAgICMgbGlzdCB0byBjb250YWluIGRhdGEudGFibGVzIAogICAgdHJ5KGR0X2xpc3RbW2ldXSA8LSBhcy5kYXRhLnRhYmxlKGNoYXJ0X2xpc3RbW2ldXSksIHNpbGVudCA9IFRSVUUpCiAgfQogIAogICMgY29udmVydCB0byBkYXRhLnRhYmxlIGFuZCBtYWtlIHN1cmUgdG8gYWRkIGEgY29sdW1uIGNvbnRhaW5pbmcgdGhlIHBhaXJzCiAgY29pbl9kdCA8LSByYmluZGxpc3QobCA9IGR0X2xpc3QsIHVzZS5uYW1lcyA9IFRSVUUsIGlkY29sID0gInBhaXIiKQogIAogICMgcmV0dXJuIGRhdGEgaW4gdXNkdCBwcmljZXMKICBpZihyZXR1cm5faW5fVVNEVCl7CiAgICAjIHRvIGdldCB0aGUgcHJpY2Ugb2YgdGhlIGFsdCBjb2luIGluIHVzZHQgaXMgbm90IHRoYXQgc2ltcGxlIGJ1dCB3ZSdsbCBkbyBpdAogICAgIyBnZXQgYSBEVCBvZiB0aGUgYnRjX3VzZHQgcGFpcgogICAgYnRjX3VzZCA8LSBjb2luX2R0W3BhaXIgPT0gIlVTRFRfQlRDIl0KICAgIGJ0Y191c2QgPC0gYnRjX3VzZFssIC4oaW5kZXgsIHBhaXIsIHdlaWdodGVkYXZlcmFnZSldCiAgICBzZXRuYW1lcyhidGNfdXNkLCBjKCJEYXRlIiwgIlVTRFRfQlRDX3BhaXIiLCAiVVNEVF9CVENfcHJpY2UiKSkKICAgIAogICAgIyBnZXQgRFQgd2l0aCBvbmx5IGFsdCBjb2lucwogICAgYWx0X2NvaW5zIDwtIGNvcHkoY29pbl9kdCkjW3BhaXIgIT0gIlVTRFRfQlRDIl0KICAgIAogICAgIyBub3cgd2UgbmVlZCB0byBhZGQgYW4gaW5kZXggdG8gdGhlIGFsdF9jb2lucyB0YWJsZSwgYnV0IGZpcnN0IHdlIGhhdmUgdG8gcmVuYW1lIHRoZSBpbmRleCBjb2x1bW4KICAgIGFsdF9jb2luc1ssIERhdGUgOj0gaW5kZXhdCiAgICBhbHRfY29pbnNbLCBpbmRleCA6PSAxOi5OXQogICAgc2V0a2V5KGFsdF9jb2lucywgaW5kZXgpCiAgICAKICAgICMgbm93IG1lcmdlIHRoZSBkYXRhIHRhYmxlcwogICAgY29pbl9kdF91c2R0IDwtIG1lcmdlKHggPSBhbHRfY29pbnMsIHkgPSBidGNfdXNkLCBieSA9ICJEYXRlIikKICAgIAogICAgIyBub3cgY2FsY3VhbHRlIHRoZSBwcmljZSBpbiB1c2R0CiAgICBjb2luX2R0X3VzZHRbLCBwcmljZV91c2R0IDo9IGlmZWxzZShwYWlyID09ICJVU0RUX0JUQyIsIFVTRFRfQlRDX3ByaWNlLCB3ZWlnaHRlZGF2ZXJhZ2UgKiBVU0RUX0JUQ19wcmljZSldCiAgICAKICAgICMgbm93IGdldCByaWQgb2YgdGhlIGV4dHJhIGNvbHVtbnMKICAgIGNvaW5fZHRfdXNkdFssIGMoIlVTRFRfQlRDX3ByaWNlIiwgIlVTRFRfQlRDX3BhaXIiKSA6PSBOVUxMXQogICAgCiAgICAjIHdlIG5lZWQgdG8gY2hhbmdlIHNvbWUgY29sdW1uIG5hbWVzCiAgICBjb2xfbmFtZXNfdG9fY2hhbmdlIDwtIGMoInBhaXIiLCAiaGlnaCIsICJsb3ciLCAib3BlbiIsICJjbG9zZSIsICJ2b2x1bWUiLCAicXVvdGV2b2x1bWUiLCAid2VpZ2h0ZWRhdmVyYWdlIikKICAgIGNvbF9uYW1lcyA8LSBuYW1lcyhjb2luX2R0X3VzZHQpCiAgICBjb2xfbmFtZXNbY29sX25hbWVzICVpbiUgY29sX25hbWVzX3RvX2NoYW5nZV0gPC0gcGFzdGUwKGNvbF9uYW1lc190b19jaGFuZ2UsICdfYnRjJykKICAgIAogICAgc2V0bmFtZXMoY29pbl9kdF91c2R0LCBjb2xfbmFtZXMpCiAgICAKICAgICMgYWRkIGEgY29sdW1uIGZvciB0aGUgdXNkdCBwYWlyCiAgICBjb2luX2R0X3VzZHRbLCBwYWlyX3VzZHQgOj0gZ3N1YigiQlRDXyIsICJVU0RUXyIsIHBhaXJfYnRjKV0KICAgIAogICAgIyBhZGp1c3QgY29sIG9yZGVyCiAgICBzZXRjb2xvcmRlcihjb2luX2R0X3VzZHQsIGMoMToxMCwgMTIsIDExKSkKICAgIAogICAgIyBzZXQga2V5IGFnYWluCiAgICBzZXRrZXkoY29pbl9kdF91c2R0LCBpbmRleCkKICAgIAogICAgIyBub3cgZ2V0IHJpZCBvZiB0aGUgaW5kZXggY29sdW1uIHNpbmNlIGl0IGlzIG5vdCBuZWVkZWQgYW55bW9yZQogICAgY29pbl9kdF91c2R0WywgaW5kZXggOj0gTlVMTF0KICAgIAogICAgIyBub3cgcHV0IHRvZ2V0aGVyIHRoZSByZXR1cm4gbGlzdCAgCiAgICByZXR1cm5fbGlzdCA8LSBsaXN0KGFsdF9jaGFydF9saXN0ID0gY2hhcnRfbGlzdCwgYWx0X2R0ID0gY29pbl9kdCwgYWx0X3VzZHRfZHQgPSBjb2luX2R0X3VzZHQpCiAgfWVsc2V7CiAgICByZXR1cm5fbGlzdCA8LSBsaXN0KGFsdF9jaGFydF9saXN0ID0gY2hhcnRfbGlzdCwgYWx0X2R0ID0gY29pbl9kdCkKICB9CiAgCiAgcmV0dXJuKHJldHVybl9saXN0KQp9CmBgYAoKVGhlIGZ1bmN0aW9uIGFib3ZlIGNhbiBiZSB1c2VkIHRvIGRvd25sb2FkIGRhdGEgZm9yIG11bHRpcGxlIGNvaW4gYXQgdGhlIHNhbWUgdGltZS4gVGhlIGZ1bmN0aW9uIHJldHVybnMgYSBkYXRhLnRhYmxlIG9iamVjdCB3aXRoIGRhdGEgZm9yIGFsbCBjb2lucyBpbiB0aGUgZnVuY3Rpb24gY2FsbC4gRXZlbiBpZiB0aGUgdXNlciBkb2Vzbid0IGFkZCBiaXRjb2luIHRvIHRoZSBsaXN0IG9mIGNvaW5zLCB0aGUgZnVuY3Rpb24gYWRkcyBiaXRjb2luIGJ5IGRlZmF1bHQuIFRoaXMgY2FuIGJlIGRlYWN0aXZhdGVkIHdpdGggdGhlIGFkZF9iaXRjb2luIGFyZ3VtZW50LiBIZXJlIGlzIGFuIGV4YW1wbGUgCmBgYHtyfQojIGdldCBhbHQgZGF0YSBmb3Igc29tZSBjb2lucwphbHRfZGF0YSA8LSBnZXRfYWx0X2RhdGEocmV0dXJuX2luX1VTRFQgPSBUCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGZyb20gPSAiMjAxNS0wMS0wMSIKICAgICAgICAgICAgICAgICAgICAgICAgICwgY29pbiA9IGMoJ0VUSCcsJ1hSUCcsICdCQ0gnLCAnTFRDJywgJ05FTycsICdYTVInLCAnREFTSCcsICdYRU0nKSlbWydhbHRfdXNkdF9kdCddXQpgYGAKCkxldCdzIGxvb2sgYXQgdGhlIGRhdGEgd2UganVzdCBkb3dubG9hZGVkCmBgYHtyfQpoZWFkKGFsdF9kYXRhKQpgYGAKVGhlIHRhYmxlIHNob3dzIHRoZSBkYXRlLCBPSExDLCBWb2x1bWUsIGFuZCB3ZWlnaHRlZGF2ZXJhZ2UgcHJpY2UgaW4gQlRDLiBJdCBhbHNvIHNob3dzIHRoZSBwYWlyIGFuZCB3ZSBhZGRlZCB0aGUgcHJpY2UgaW4gVVNELgoKIyMgQml0Y29pbi1BbHRjb2lucyBDb3JyZWxhdGlvbnMKV2hlbmV2ZW4gSSBsb29rIGF0IHRoZSBwcmljZXMgb2YgdGhlIGNvaW5zIGF2YWlsYWJsZSBvbiBteSBbY29pbmJhc2VdKGh0dHBzOi8vd3d3LmNvaW5iYXNlLmNvbS8pIGFwcCBJIGFsd2F5cyBnZXQgc3RydWNrIGJ5IHRoZSBzaW1pbGFyaXR5IG9mIHRoZSBwcmljZSB0cmVuZHMgYmV0d2VlbiB0aGUgZm91ciBjb2lucyBhdmFpbGFibGUgb24gY29pbmJhc2U6IEJUQywgRVRILCBCQ0gsIGFuZCBMVEMsIHNlZSBGaWd1cmUgYmVsb3cuIFNvIEkgdGhvdWdodCBpdCB3aWxsIGJlIGEgZ29vZCBpZGVhIHRvIGV4cGxvcmUgdGhlIGNvcnJlbGF0aW9uIGluIHByaWNlIHRyZW5kcyBiZXR3ZWVuIGFsdGNvaW5zIGFuZCBiaXRjb2luLiAKCiFbQXBwYXJlbnQgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgcHJpY2VzIG9mIEJpdGNvaW4gYW5kIG90aGVyIGNvaW5zIG9uIGNvaW5iYXNlLl0oLi4vZmlndXJlcy9jb2luYmFzZV9zY3JlZW5zaG90LmpwZyl7d2lkdGg9NTAwcHh9CgpMZXQncyBsb29rIGF0IHByaWNlIHRyZW5kcyBvZiB0aGUgY29pbnMgd2UganVzdCBkb3dubG9hZGVkLiBUbyBiZXR0ZXIgc2VlIHBvdGVudGlhbCBjb3JyZWxhdGlvbnMgSSBhbSBnb2luZyB0byBvbmx5IHpvb24gaW4gb24gMjAxOC4gCgpgYGB7ciBjb2luX3ByaWNlc18yMDE4LCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NiwgZmlnLmNhcD0iUHJpY2VzIG9mIEJpdGNvaW4gYW5kIG90aGVyIGFsdGNvaW5zIGluIDIwMTgifQpwIDwtIGdncGxvdChhbHRfZGF0YVt5ZWFyKERhdGUpID09IDIwMThdLCBhZXMoeCA9IERhdGUsIHkgPSAgcHJpY2VfdXNkdCwgY29sID0gcGFpcl91c2R0KSkgKyBnZW9tX2xpbmUoKQpwIDwtIHAgKyBmYWNldF93cmFwKH5wYWlyX3VzZHQsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDMpICsgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyB5bGFiKCJQcmljZSAoVVNEKSIpCnAKYGBgCgpUaGUgZmlndXJlIGFib3ZlIHNob3dzIHRoYXQgc29tZSBjb2lucyBzZWVtcyB0byBiZSBtb3JlIGNvcnJlbGF0ZWQgd2l0aCBCaXRjb2luIHRoYW4gb3RoZXJzLiBUaGUgZmlndXJlIGFsc28gc2hvd3MgdGhhdCB0aGlzIHZhcmlhYmxpdHkgYmV0d2VlbiBCaXRjb2luIGFuZCBhbm90aGVyIGNvaW4gdmFyaWVzIG92ZXIgdGltZS4gTW9yZSBvbiB0aGlzIGJlbG93LgoKVHlyaW5nIHRvIGZpbmQgY29ycmVsYXRpb25zIGJld3RlZW4gdGltZSBzZXJpZXMgZGF0YSB1c2luZyBQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG9yIG90aGVyIG1ldHJpY3MgdXNlZCB3aXRoIHN0YXRpb25hcnkgZGF0YSwgdGltZSBzZXJpZXMgaXMgbm90IGEgZm9ybSBvZiBzdGF0aW9uYXJ5IGRhdGEsIGNhbiBnaXZlIG1pc2xlYWRpbmcgcmVzdWx0cy4gU2ltaWxhciB0cmVuZHMgaW4gdGltZSBzZXJpZXMgZGF0YSBjYW4gYWxzbyBiZSB2ZXJ5IG1pc2xlYWRpbmcsIGEgbmljZSBhcnRpY2xlIG9uIHRoaXMgdG9waWMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3N2ZHMuY29tL2F2b2lkaW5nLWNvbW1vbi1taXN0YWtlcy13aXRoLXRpbWUtc2VyaWVzLykuIEFuZCBhbHdheXMgcmVtZW1iZXIgdGhhdCAqKkNvcnJlbGF0aW9uIGRvZXNuJ3QgZ3VhcmFudGVlIENhdXNhdGlvbioqCgpCb3R0b20gbGluZSBpcyB0aGUgZm9sbG93aW5nLCBvbmUgaGFzIHRvIGJlIGNhcmVmdWwgd2hlbiBjcm9zcy1jb3JyZWxhdGluZyB0aW1lIHNlcmljZS4gSW4gb3JkZXIgdG8gcGVyZm9ybSBwcm9wZXIgY29ycmVsYXRpb24gYW5hbHlzaXMgd2UgbmVlZCB0byBhZGQgc29tZSBuZXcgdmFyaWFibGVzIHRvIG91ciB0YWJsZS4KCiMjIyBQZXJjZW50YWdlIERhaWx5IENoYW5nZQpQZXJjZW50YWdlIGRhaWx5IGNoYW5nZSBjYWxjdWxhdGVzIHRoZSBwcmljZSBjaGFuZ2Ugb2YgYSBjb2luIG92ZXIgYSBwZXJpb2Qgb2YgYSBkYXkuIExldCdzIGFkZCB0aGF0IHRvIHRoZSB0YWJsZS4gTm90aWNlIHRoYXQgd2UgYXJlIGNhbGN1YWx0aW5nIHRoaXMgdmFyaWFibGUgdXNpbmcgdGhlIFVTRCBwcmljZSwgYW5kIG5vdCB0aGUgcHJpY2UgaW4gQml0Y29pbi4gCgpgYGB7cn0KIyBhZGQgZGFpbHkgcHJpY2UgY2hhbmdlCmFsdF9kYXRhWywgcGN0X2NoYW5nZSA6PSBEZWx0KHByaWNlX3VzZHQpLCBieSA9IHBhaXJfdXNkdF0KYGBgCgojIyMgTm9ybWFsaXplZCBQcmljZSBpbiBVU0QKU2luY2UgdGhlIHByaWNlcyB2YXJ5IGEgbG90LCBib3RoIG92ZXJ0aW1lIGZvciB0aGUgc2FtZSBjb2luIGFuZCBiZXR3ZWVuIGNvaW5zLCB3ZSB3aWxsIGFkZCBhIHZhcmlhYmxlIG9mIHRoZSBub3JtYWxpemVkIHByaWNlIGluIFVTRC4gVGhpcyB2YXJpYWJsZSB3aWxsIG1ha2UgaXQgZWFzeSB0byBwbG90IHByaWNlcyBvZiBjb2lucyBvbiB0aGUgc2FtZSBmaWd1cmUuIAoKYGBge3J9CiMgYWRkIG5vcm1hbGl6ZWQgcHJpY2VzIGluIHVkc3QKYWx0X2RhdGFbLCBwcmljZV91c2R0X25vcm0gOj0gcHJpY2VfdXNkdC9tYXgocHJpY2VfdXNkdCksIGJ5ID0gcGFpcl91c2R0XQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgdGhlIG5vcm1hbGl6ZWQgcHJpY2VzIGluIFVTRCwgbGV0J3MgbG9vayBhdCB0aGUgcHJpY2VzIG9mIGJpdGNvaW4gYW5kIGxpdGNvaW4gb24gdGhlIHNhbWUgZmlndXJlLiBXZSdsbCBkbyB0aGF0IGZvciAyMDE4IHNvIHdlIGNhbiBiZXR0ZXIgc2VlIGFueSBwb3NzaWJsZSBjb3JyZWxhdGlvbnMuIAoKYGBge3IgYnRjX2x0Y18yMDE4LCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NiwgZmlnLmNhcD0iUHJpY2VzIG9mIEJpdGNvaW4gYW5kIExUQyBpbiAyMDE4In0KcCA8LSBnZ3Bsb3QoYWx0X2RhdGFbeWVhcihEYXRlKSA9PSAyMDE4ICYgcGFpcl91c2R0ICVsaWtlJSAiQlRDfExUQyJdLCBhZXMoeCA9IERhdGUsIHkgPSAgcHJpY2VfdXNkdF9ub3JtLCBjb2wgPSBwYWlyX3VzZHQpKSArIGdlb21fbGluZSgpCnAgPC0gcCArIHRoZW1lX21pbmltYWwoKSArIHlsYWIoIlByaWNlIChVU0QpIikKZ2dwbG90bHkocCkKYGBgCgpUaGUgdHJlbmRzIGluIHRoZSBwcmljZXMgb2YgQlRDIGFuZCBMVEMgYXJlIHZlcnkgc2ltaWxhciwgTGV0J3MgbG9vayBhdCBwcmljZSB0cmVuZHMgZm9yIDIwMTcKCmBgYHtyIGJ0Y19sdGNfMjAxNywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTYsIGZpZy5jYXA9IlByaWNlcyBvZiBCaXRjb2luIGFuZCBMVEMgaW4gMjAxNyJ9CnAgPC0gZ2dwbG90KGFsdF9kYXRhW3llYXIoRGF0ZSkgPT0gMjAxNyAmIHBhaXJfdXNkdCAlbGlrZSUgIkJUQ3xMVEMiXSwgYWVzKHggPSBEYXRlLCB5ID0gIHByaWNlX3VzZHRfbm9ybSwgY29sID0gcGFpcl91c2R0KSkgKyBnZW9tX2xpbmUoKQpwIDwtIHAgKyB0aGVtZV9taW5pbWFsKCkgKyB5bGFiKCJQcmljZSAoVVNEKSIpCmdncGxvdGx5KHApCmBgYAoKU2VlbXMgbGlrZSB3ZSBuZWVkIHRvIHpvb24gaW4gb24gdGhlIGxhc3QgcXVhcnRlciBvZiAyMDE3LCBsZXQncyBkbyB0aGF0CgpgYGB7ciBidGNfbHRjXzIwMTdfM3JkX3F1YXJ0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuY2FwPSJQcmljZXMgb2YgQml0Y29pbiBhbmQgTFRDIGluIHRoZSBsYXN0IHF1YXJ0ZXIgb2YgMjAxNyJ9CnAgPC0gZ2dwbG90KGFsdF9kYXRhW0RhdGUgPj0gIjIwMTctMTAtMDEiICAmIERhdGUgPCAiMjAxOC0wMS0wMSIgJiAgcGFpcl91c2R0ICVsaWtlJSAiQlRDfExUQyJdLCBhZXMoeCA9IERhdGUsIHkgPSAgcHJpY2VfdXNkdF9ub3JtLCBjb2wgPSBwYWlyX3VzZHQpKSArIGdlb21fbGluZSgpCnAgPC0gcCArIHRoZW1lX21pbmltYWwoKSArIHlsYWIoIlByaWNlIChVU0QpIikKZ2dwbG90bHkocCkKYGBgCgpJdCBpcyBjbGVhciBmcm9tIHRoZSBhYm92ZSBmaWd1cmUgdGhhdCB0aGUgY29ycmVsYXRpb24gaW4gdGhlIHByaWNlcyBvZiBiaXRjb2luIGFuZCBMVEMgdmFyeSBvdmVyIHRpbWUuIE5vdGUgaG93IHRoZSBoaWdoZXN0IHByaWNlIGZvciBiaXRjb2luIG9uIERlY2VtYmVyIDE3IDIwMTcsIHByZWNlZWRlZCB0aGF0IG9mIExUQyBieSB0d28gZGF5cywgd2hpY2ggb2NjdXJlZCBvbiBEZWNlbWJlciAxOSAyMDE3LiBUaGlzIHdhc24ndCB0aGUgY2FzZSBmb3IgdGhlIEFUSCB3aGljaCBvY2N1cnJlZCBvbiBKYW51YXJ5IDZ0aCAyMDE4IGZvciBib3RoIGNvaW5zLiAgIAoKIyMgU3RhdGljIENvcnJlbGF0aW9ucyAoYW5kIHdoeSB5b3Ugc2hvdWxkbid0IHVzZSB0aGVtIHdpdGggY3J5cHRvISkKClVwIHVudGlsIG5vdyBJIGhhdmVuJ3QgY2FsY3VsYXRlZCBhbnkgY29ycmVsdGFpb25zIGJldHdlZW4gdGhlIHByaWNlIG9mIGRpZmZlcmVudCBjb2lucy4gWW91IG1pZ2h0IGFzayB3aHkgc2hvdWxkIHdlIGV2ZW4gY2FyZSBhYm91dCBjb3JyZWxhdGlvbnMgaW4gdGltZSBzZXJpZXMuIFdlbGwsIGluIHRoZSBjYXNlIG9mIGZpbmFuY2lhbCB0aW1lIHNlcmllcyBkYXRhLCBpZiBvbmUgY2FuIHNob3cgdGhhdCBhIGNvcnJlbGF0aW9uIGV4aXN0cyBiZXR3ZWVuIHR3byB0aW1lIHNlcmllcyB0aGVuIG9uZSBjYW4gdXNlIHRoaXMgY29ycmVsYXRpb24gdG8gbW9kZWwvcHJlZGljdCB0aGUgcHJpY2UgbW92ZW1lbnQgb2Ygb25lIGNvaW4vc3RvY2sgZ2l2ZW4gdGhlIHByaWNlIHRyZW5kcyBvZiBhbm90aGVyIGNvaW4vc3RvY2suIEhvd2V2ZXIsIGFzIHdlIG1lbnRpb25lZCBlYXJsaWVyLCBjb3JyZWxhdGlvbiBmb3IgdGltZSBzZXJpZXMgZGF0YSBpcyBub3Qgc3RhdGljLCBpdCBjaGFuZ2VzIG92ZXIgdGltZS4gQWN0dWFsbHkgbGV0J3Mgc2hvdyB0aGF0LiBUbyBkbyB0aGF0IEkgYW0gZ29pbmcgdG8gYmUgY2FsY3VsYXRpbmcgdGhlIFtQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QZWFyc29uX2NvcnJlbGF0aW9uX2NvZWZmaWNpZW50KS4gSW4gc2ltcGxlIHdvcmRzLCBQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGZvciB0d28gdmVjdG9ycyBvZiBkYXRhIGlzIGEgbWVhc3VyZSB0aGF0IHNob3dzIGhvdyBjb3JyZWxhdGVkIHRoZXNlIHR3byB2ZWN0b3JzIG9mIGRhdGEgYXJlLiBUaGUgdmFsdWUgb2YgdGhpcyBjb2VmZmljaWVudCB2YXJpZXMgZnJvbSAtMSwgcGVyZmVjdGx5IGFudGktY29ycmVsYXRlZCwgdG8gMSwgcGVyZmVjdGx5IGNvcnJlbGF0ZWQuIFNvIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3IgYSBzZXJpZXMgb2YgbnVtYmVycyBvbiBpdHNlbGYgaXMgMS4gQSB2YWx1ZSBvZiB6ZXJvIG1lYW5zIHRoZXNlIGlzIG5vIGNvcnJlbGF0aW9uLiBSZW1lbWJlciwgdGhpcyBvbmx5IHdvcmtzIGZvciBzdGF0aWMgZGF0YS4gCgpJbiBvcmRlciB0byBwZXJmb3JtIGNvcnJlbGF0aW9uIG9uIG91ciBkYXRhIEkgYW0gZ29pbmcgdG8gbmVlZCB0byBkbyBzb21lIGRhdGEgdHJhbnNmb3JtYXRpb246CgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIHN1YnNldCBkYXRhLCBvbmx5IGtlZXAgdGhlIGRhdGUsIHRoZSBwYWlyLCBhbmQgdGhlIHByaWNlCmFsdF9kYXRhX3N1YiA8LSBhbHRfZGF0YVssIC4oRGF0ZSwgcGFpcl91c2R0LCBwcmljZV91c2R0KV0KCiMgY29udmVydCB0byB3aWRlIGZvcm1hdCAKYWx0X2RhdGFfc3ViIDwtIHNwcmVhZChkYXRhID0gYWx0X2RhdGFfc3ViLCBrZXkgPSAicGFpcl91c2R0IiwgdmFsdWUgPSAicHJpY2VfdXNkdCIpCgojIGNsZWFuIGNvbHVtbiBuYW1lcwpzZXRuYW1lcyhhbHRfZGF0YV9zdWIsIGdzdWIoIlVTRFRfIiwgIiIsIGNvbG5hbWVzKGFsdF9kYXRhX3N1YikpKQpgYGAKClRoZSBuZXcgdGFibGUgd2UgY3JlYXRlZCBjb250YWlucyB0aGUgZGF0ZSBhbG9uZyB3aXRoIHRoZSBwcmljZXMgaW4gVVNEVCBmb3IgZWFjaCBjb2luIHdlIGhhdmUgaW4gb3VyIHRhYmxlLiAKCmBgYHtyfQp0YWlsKGFsdF9kYXRhX3N1YikKYGBgCgpBZ2Fpbiwgd2hhdCBJIGFtIGRvaW5nIGhlcmUgaXMgbm90IGNvcnJlY3QsIEkgYW0ganVzdCB0cnlpbmcgdG8gc2hvdyB5b3Ugd2h5IHdlIHNob3VsZG4ndCBiZSBkb2luZyBzdGF0aWMgY29ycmVsYXRpb25zIG9uIGNyeXB0byBkYXRhLiAKTm93IHdlJ2xsIGNhbGN1bGF0ZSB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIHRoZSBjb2lucyB3ZSBoYXZlLCB0aGVuIHdlIGFyZSBnb2luZyB0byBtYWtlIGEgbmljZSBwbG90IG9mIHRoZXNlIGNvZWZmaWNpZW50cy4gCgpgYGB7ciBmaWcud2lkdGg9OH0KIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApNIDwtIGNvcihhbHRfZGF0YV9zdWJbLCAtMV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSAjIG5vdGljZSBob3cgd2UgYXJlIGlnbm9yaW5nIG1pc3NpbmcgZGF0YSB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50CgojIHBsb3QgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JycGxvdC5taXhlZChjb3JyID0gTSwgdXBwZXIgPSAiZWxsaXBzZSIsIGxvd2VyID0gIm51bWJlciIsIG9yZGVyID0gIkFPRSIsIHRsLmNvbCA9ICJibGFjayIpCmBgYApUaGUgZmlndXJlIGFib3ZlIHNob3dzIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGNvaW5zLiBJdCBpcyBlYXN5IHRvIHJlYWQsIHZpc3VhbGx5LCB0aGUgZGFya2VyIHRoZSBjb2xvciBvZiB0aGUgZWxsaXBzZSwgYW5kIHRoZSBtb3JlIGRpYWdvbmFsIHRoZSBlbGxpcHNlLCB0aGUgaGlnaGVyIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudC4gT2YgY291cnNlIHlvdSBjYW4gYWxzbyBqdXN0IGxvb2sgYXQgdGhlIG51bWJlcnMgb24gdGhlIGJvdHRvbSBsZWZ0IHBhcnQgb2YgdGhlIGZpZ3VyZSB0byBnZXQgdGhlIHZhbHVlIG9mIHRoZSBjb2VmZmljaWVudCBiZXR3ZWVuIHR3byBjb2lucyA6KS4gVGhlIGZpZ3VyZSBzaG93cyBob3cgaGlnaGx5IGNvcnJlbGF0ZWQgdGhlIHByaWNlcyBvZiBjcnlwdG8gY3VycmVuY2llcyBjYW4gYmUuIEZvciBleGFtcGxlIFhSUCBhbmQgWEVNIGhhdmUgYSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBvZiAwLjkzLiBUaGUgaGlnaGVzdCBjb3JyZWxhdGlvbiBzZWVtcyB0byBiZSBiZXR3ZWVuIEJDSCBhbmQgREFTSCBhdCAwLjk3IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50LiAKCkFsbCBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgd2Ugc2VlIGluIHRoZSBhYm92ZSBmaWd1cmUgYXJlIHNpZ25pZmljYW50LCB0aGUgcXVlc3Rpb24gaXMsIGRvIHRoZXNlIGNvcnJlbGF0aW9ucyB2YXJ5IG92ZXIgdGltZS4gVG8gYW5zd2VyIHRoaXMgcXVlc3Rpb24gSSB3aWxsIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmV0d2VlbiBCaXRjb2luIGFuZCBEQVNIIG9uIGEgbW9udGhseSBiYXNpcywgeW91IGNhbiBkbyB0aGF0IGZvciBhbnkgdGltZSBwZXJpb2QsIGFuZCB3aWxsIHNob3cgdGhhdCB0aGlzIGNvZWZmaWNpZW50IHZhcmllcyBncmVhdGx5IG92ZXIgdGltZS4gTGV0J3MgZG8gdGhhdAoKYGBge3IgZmlnLndpZHRoPTh9CiMgc3Vic2V0IHRoZSBkYXRhCmJ0Y19kYXNoIDwtIGFsdF9kYXRhX3N1YlssIC4oRGF0ZSwgQlRDLCBEQVNIKV0KCiMgYWRkIGEgeWVhcl9tb250aCBjb2x1bW4KYnRjX2Rhc2hbLCB5ZWFyX21vbnRoIDo9IGFzLnllYXJtb24oRGF0ZSldCgojIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb24gbW9udGx5IGJhc2lzCmJ0Y19kYXNoXzIgPC0gYnRjX2Rhc2hbLCBjb3IoQlRDLCBEQVNIKSwgYnkgPSB5ZWFyX21vbnRoXQoKIyBub3cgcGxvdCB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYXMgYSBmdW5jdGlvbiBvZiBtb250aCBhbmQgeWVhcgpwbG90KGJ0Y19kYXNoXzIkeWVhcl9tb250aCwgYnRjX2Rhc2hfMiRWMSwgeGxhYiA9ICJZZWFyLU1vbnRoIiwgbWFpbiA9ICJDb3JyZWxhdGlvbiBDb2VmZi4gQmV0d2VlbiBCVEMgYW5kIERBU0ggT3ZlciB0aW1lIgogICAgICwgeWxhYiA9ICJDb3JyZWxhdGlvbiBDb2VmZmljaWVudCIsIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCBjb2wgPSBpZmVsc2UoYnRjX2Rhc2hfMiRWMSA+IDAsICJibHVlIiwgInJlZCIpCiAgICAgLCB5bGltID0gYygtMSwgMSkpCmBgYAoKVGhpcyBpcyBpbnRlcmVzdGluZywgdGhlIHZhbHVlIG9mIHRoZSBtb250aGx5IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGJldHdlZW4gYml0Y29pbiBhbmQgREFTSCB2YXJpZXMgYmV0d2VlbiAqKjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPmByIHJvdW5kKHJhbmdlKGJ0Y19kYXNoXzIkVjEpWzFdLCAyKWA8L3NwYW4+KiosIGhpZ2hseSBhbnRpLWNvcnJlbGF0ZWQsIHRvICoqPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPmByIHJvdW5kKHJhbmdlKGJ0Y19kYXNoXzIkVjEpWzJdLCAyKWA8L3NwYW4+KiosIGhpZ2hseSBjb3JyZWxhdGVkLiBBbmQgdGhpcyBpcyB3aHkgKipfeW91IHNob3VsZCBuZXZlciB1c2Ugc3RhdGljIGNvcnJlbGF0aW9uIG1ldHJpY3Mgd2l0aCBjcnlwdG8gZGF0YSFfKiogCgoKIyMjIENvcnJlbGF0aW9uIE5ldHdvcmtzClRoZXJlIGlzIG9uZSBtb3JlIHBsb3QgSSB3b3VsZCBsaWtlIHRvIG1ha2UsIHdoaWNoIGlzIGEgbmV0d29yayBwbG90IG9mIHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGNvaW5zLiBUaGUgY29ycmVsYXRpb24gbmV0d29yayBwbG90IGhlbHBzIHNob3cgc3RyZW5ndGhzIG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGRpZmZlcmVudCBjb2lucy4gQWdpYW4sIHRoZXNlIGNvcnJlbGF0aW9ucyBhcmUgdGltZSBkZXBlbmRlbnQgYW5kIHRoZSBmaWd1cmUgd2Ugd2lsbCBiZSBtYWtpbmcgd2lsbCBjaGFuZ2Ugb3ZlciB0aW1lLCBidXQgSSBzdGlsbCB0aGluayBpdCBpcyBhIGdvb2QgZmlndXJlIHRvIG1ha2UuIEhlcmUgaXQgaXM6CgpgYGB7ciBmaWcud2lkdGg9OH0KIyB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBncmVhdCBjb3JyciBwYWNrYWdlIGZvciB0aGlzIHdvcmsKIyBnZXQgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCwganVzdCBsaWtlIHdlIGRpZCBiZWZvcmUKIyBidWlsZSB0aGUgY29ycmVsYXRpb24gbWF0cml4IAojIHRoZSBjb2RlIHNuaXBwZXRzIGJlbG93IGFyZSB0YWtlbiBmcm9tLCB0aGF0IGlzIGEgZ3JlYXQgYmxvZyBCVFcgCiMgaHR0cDovL3d3dy5idXNpbmVzcy1zY2llbmNlLmlvL3RpbWVzZXJpZXMtYW5hbHlzaXMvMjAxNy8wNy8zMC90aWR5LXRpbWVzZXJpZXMtYW5hbHlzaXMtcHQtMy5odG1sCmNvcnJfMiA8LSBjb3JyZWxhdGUoYWx0X2RhdGFfc3ViWywgLTFdKQoKIyBtYWtlIHRoZSBuZXR3b3JrIHBsb3QKIyBOZXR3b3JrIHBsb3QKY29ycl9uZXQgPC0gY29ycl8yICU+JQogIG5ldHdvcmtfcGxvdChjb2xvdXJzID0gYyhwYWxldHRlX2xpZ2h0KClbWzJdXSwgIndoaXRlIiwgcGFsZXR0ZV9saWdodCgpW1s0XV0pLCBsZWdlbmQgPSBUUlVFKSArCiAgbGFicygKICAgIHRpdGxlID0gIlN0YXRpYyBDb3JyZWxhdGlvbnMgb2Ygc29tZSBDcnlwdG8gQ3VycmVuY2llcyIsCiAgICBzdWJ0aXRsZSA9ICIyMDE0IHRocm91Z2ggMjAxOCIKICApICsKICB0aGVtZV90cSgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKY29ycl9uZXQKYGBgCgpUaGUgZmlndXJlIGFib3ZlIHNob3dzIGEgbmV0d29yayB3aGljaCBtZWFzdXJlcyBob3cgc3Ryb25nbHkgY29ycmVsYXRlZCB0aGUgcHJpY2VzIG9mIHRoZSBjb2lucyB1bmRlciBzdHVneSBhcmUuIFRoZSBkYXJrZXIgdGhlIGNvbG9yIG9mIHRoZSBlZGdlLCBsaW5lLCBjb25uZWN0aW5nIHR3byBjb2lucyBhbmQgdGhlIGNsb3NlciB0aGV5IGFyZSBpbiB0aGUgbmV0d29yayB0aGUgc3Ryb25nZXIgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlc2UgdHdvIGNvaW5zLiAKCkZyb20gdGhlIGZpZ3VyZSwgaXQgc2VlbXMgbGlrZSBYTVIsIExUQywgYW5kIEJUQyBhcmUgaW4gdGhlIGhlYXJ0IG9mIHRoaXMgbmV0d29yaywgd2hpbGUgQkNIIHNlZW1zIHRvIGJlIHRoZSBsZWFzdCBjb3JyZWxhdGVkIHdpdGggdGhlIHJlc3Qgb2YgdGhlIGNvaW5zLiAKTGV0J3Mgc2VlIGhvdyB0aGUgbmV0d29yayBwbG90IGNoYW5nZXMgYmV0d2VlbiAyMDE3IGFuZCAyMDE4OgoKYGBge3IgY29ycl9uZXRfMjAxNy0yMDE4LCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9MTJ9CiMgc3Vic2V0IHRoZSBkYXRhIGFuZCBnZXQgY29ycmVsYXRpb24gbWF0cmljZXMKY29ycl8yMDE3IDwtIGNvcnJlbGF0ZShhbHRfZGF0YV9zdWJbeWVhcihEYXRlKSA9PSAyMDE3XVssIC0xXSkKY29ycl8yMDE4IDwtIGNvcnJlbGF0ZShhbHRfZGF0YV9zdWJbeWVhcihEYXRlKSA9PSAyMDE4XVssIC0xXSkKCiMgYnVpbGQgTmV0d29yayBwbG90cwpjb3JyX25ldF8yMDE3IDwtIGNvcnJfMjAxNyAlPiUKICBuZXR3b3JrX3Bsb3QoY29sb3VycyA9IGMocGFsZXR0ZV9saWdodCgpW1syXV0sICJ3aGl0ZSIsIHBhbGV0dGVfbGlnaHQoKVtbNF1dKSwgbGVnZW5kID0gVFJVRSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJTdGF0aWMgQ29ycmVsYXRpb25zIG9mIHNvbWUgQ3J5cHRvIEN1cnJlbmNpZXMiLAogICAgc3VidGl0bGUgPSAiMjAxNyIKICApICsKICB0aGVtZV90cSgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmNvcnJfbmV0XzIwMTggPC0gY29ycl8yMDE4ICU+JQogIG5ldHdvcmtfcGxvdChjb2xvdXJzID0gYyhwYWxldHRlX2xpZ2h0KClbWzJdXSwgIndoaXRlIiwgcGFsZXR0ZV9saWdodCgpW1s0XV0pLCBsZWdlbmQgPSBUUlVFKSArCiAgbGFicygKICAgIHRpdGxlID0gIlN0YXRpYyBDb3JyZWxhdGlvbnMgb2Ygc29tZSBDcnlwdG8gQ3VycmVuY2llcyIsCiAgICBzdWJ0aXRsZSA9ICIyMDE4IgogICkgKwogIHRoZW1lX3RxKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKIyBjb21iaW5lIG5ldHdvcmsgcGxvdHMKY293X25ldF9wbG90cyA8LXBsb3RfZ3JpZChjb3JyX25ldF8yMDE3LCBjb3JyX25ldF8yMDE4LCBuY29sID0gMikKCnRpdGxlIDwtIGdnZHJhdygpICsgCiAgICBkcmF3X2xhYmVsKGxhYmVsID0gJ0NyeXB0byBDb3JyZWxhdGlvbiBOZXR3b3JrcycsCiAgICAgICAgICAgICAgIGZvbnRmYWNlID0gJ2JvbGQnLCBzaXplID0gMTgpCmNvd19vdXQgPC0gcGxvdF9ncmlkKHRpdGxlLCBjb3dfbmV0X3Bsb3RzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMC4xLCAxKSkKY293X291dApgYGAKCkFzIGNhbiBiZSBzZWVuLCB0aGUgY29ycmVsYXRpb24gbmV0d29ya3MgZG8gY2hhbmdlIG92ZXJ0aW1lLiBUaGlzIGlzIG5vdCBuZXdzIHNpbmNlIHdlIGFscmVhZHkgc2F3IGluIHRoZSBwcmV2aW91cyBzZWN0aW9uIHRoYXQgdGhlIHZhbHVlIG9mIHRoZSBjb3JyZWxhdGlvbiB2YXJpZXMgb3ZlcnRpbWUgKEkga25vdyB3ZSBzaG93ZWQgdGhpcyB0byBiZSB0cnVlIGZvciB0aGUgQlRDLURBU0ggYWlyIGJ1dCB3ZSdsbCBzaG93IHRoYXQgdGhpcyBpcyB0cnVlIGZvciB0aGUgcmVzdCBvZiB0aGUgY29pbnMgaW4gdGhlIG5leHQgc2VjdGlvbi4pCgojIyMgRGFpbHkgUmV0dXJucyBDb3JyZWxhdGlvbnMKTGV0J3MgbG9vayBhdCB0aGUgcGVyY2VudGFnZSBkYWlseSBjaGFuZ2VzIG9mIHRoZSBhbHRjb2lucyBiZXR3ZWVuIDIwMTUgYW5kIHRvZGF5LgoKYGBge3IgcGVyY2VudGFnZV9kYWlseV9jaGFuZ2UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuY2FwPSJQZXJjZW50YWdlIGRhaWx5IHJldHVybnMgZm9yIHNvbWUgY29pbnMifQojIHBsb3QgdGhlIHBlcmNlbnQgY2hhbmdlcwpwIDwtIGdncGxvdChhbHRfZGF0YVtEYXRlID4geW1kKCIyMDE1LTAxLTAxIildLCBhZXMoeCA9IERhdGUsIHkgPSAgKDEwMCpwY3RfY2hhbmdlKSwgY29sID0gcGFpcl91c2R0KSkgKyBnZW9tX2xpbmUoKQpwIDwtIHAgKyBnZ3RpdGxlKCIlIERhaWx5IFJldHVybnMgb3ZlciB0aW1lIikgKyB5bGFiKCJEYWlseSBSZXR1cm4gKCUpIikgCnAgPC0gcCArIHRoZW1lX2J3KCkgKyBndWlkZXMoY29sPWd1aWRlX2xlZ2VuZCh0aXRsZT0iQ29pbiBQYWlyIikpCmdncGxvdGx5KHApCmBgYAoKQWx0aG91Z2ggdGhlIGFib3ZlIGZpZ3VyZSBpcyB2ZXJ5IGNsdXR0ZXJlZCwgb25lIHRoaW5nIGlzIGNlcnRhaW4sIHBlcmNlbnRhZ2UgZGFpbHkgcmV0dXJucyB2YXJ5IGdyZWF0bHkgZm9yIGNyeXB0by4gTGV0J3MgdHJ5IHRvIG1ha2UgdGhpcyBmaWd1cmUgYSBiaXQgZWFzaWVyIHRvIHJlYWQKCmBgYHtyIHBlcmNlbnRhZ2VfZGFpbHlfY2hhbmdlXzIsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIGZpZy5jYXA9IlBlcmNlbnRhZ2UgZGFpbHkgcmV0dXJucyBmb3Igc29tZSBjb2lucyJ9CnAgPC0gZ2dwbG90KGFsdF9kYXRhW0RhdGUgPiB5bWQoIjIwMTUtMDEtMDEiKV0sIGFlcyh4ID0gRGF0ZSwgeSA9ICAoMTAwKnBjdF9jaGFuZ2UpLCBjb2wgPSBwYWlyX3VzZHQpKSArIGdlb21fbGluZSgpICsgZmFjZXRfd3JhcCh+IHBhaXJfdXNkdCkKcCA8LSBwICsgZ2d0aXRsZSgiUGVyY2VudGFnZSBEYWlseSBSZXR1cm5zIG92ZXIgdGltZSIpICsgeWxhYigiRGFpbHkgUmV0dXJuICglKSIpIApwIDwtIHAgKyB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgCmdncGxvdGx5KHApCmBgYAoKSXQgaXMga2luZCBvZiBzdXJwcmlzaW5nIHRoYXQgQml0Y29pbiBoYXMgdGhlIGxlYXN0IHZhcmlhYmlsaXR5IGluIGRhaWx5IHJldHVybnMuIFRoZSBuaWNlIGJpZyBzcGlrZSBhcm91bmQgQXByaWwgMm5kIDIwMTcgc2hvd3MgYSBwZXJjZW50YWdlIGRhaWx5IHJldHVybiBvZiB+ODglIGZvciBYUlAsIHRoaXMgaXMgdGhlIGhpZ2hlc3QgZGFpbHkgcmV0dXJuIEkgaGF2ZSBzZWVuISAKCkxldCdzIGxvb2sgYXQgdGhlIHBlcmNlbnRhZ2UgZGFpbHkgcmV0dXJucyBmb3IgQml0Y29pbiBhbmQgTGl0ZWNvaW4gc2luY2UgdGhleSBzZWVtIHRvIGJlIGhpZ2hseSBjb3JyZWxhdGVkLiBJIGFtIGdvaW5nIHRvIHpvb20gaW4gb24gdGhlIHRpbWUgcGVyaW9kIDIwMTYtMDItMDEgYW5kIDIwMTYtMDUtMDEuCgpgYGB7ciBkYWlseV9yZXR1cm5fbHRjX2J0YywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTYsIGZpZy5jYXA9IkRhaWx5IFJldHVybiBmb3IgQml0Y29pbiBhbmQgTFRDIGluIDIwMTgifQpzdGFydF9kYXRlIDwtIHltZCgiMjAxNi0wMi0wMSIpCmVuZF9kYXRlIDwtIHltZCgiMjAxNi0wNS0wMSIpCnAgPC0gZ2dwbG90KGFsdF9kYXRhW3BhaXJfdXNkdCAlbGlrZSUgIkJUQ3xMVEMiICYgRGF0ZSA+IHN0YXJ0X2RhdGUgJiBEYXRlIDwgZW5kX2RhdGVdLCBhZXMoeCA9IERhdGUsIHkgPSAgKDEwMCpwY3RfY2hhbmdlKSwgY29sID0gcGFpcl91c2R0KSkgKyBnZW9tX2xpbmUoKSArIHRoZW1lX2J3KCkgKyB5bGFiKCJQcmljZSAoVVNEKSIpCnAKYGBgCgpUaGUgZmlndXJlIHNob3dzIGNsZWFyIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGRhaWx5IHJldHVybnMgb2YgQml0Y29pbiBhbmQgbGl0Y29pbi4gSXQgYWxzbyBzaG93cyB0aGF0IHRoZXNlIGNvcnJlbGF0aW9ucyBjYW4gdmFyeSBvdmVydGltZS4gSW4gZmFjdCwgbGV0J3MgbG9vayBhdCBob3cgdGhlc2UgY29ycmVsYXRpb25zIHZhcnkgb3ZlcnRpbWUuIAoKYGBge3J9CiMgdGhlc2Ugc3RlcHMgYXJlIHNpbWlsYXIgdG8gdGhlIG9uZXMgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24sIHRoZSBvbmx5IGRpZmZlcm5lY3QgaXMgdGhhdCBub3cgd2UgYXJlIGxvb2tpbmcgYXQgdGhlIHBlcmNlbnRhZ2UgY2hhbmdlIGluIHByaWNlIGRpZmZlcmVuY2Ugb24gZGFpbHkgYmFzaXMgaW5zdGVhZCBvZiB0aGUgYWN0dWFsIHByaWNlCgojIHN1YnNldCBkYXRhLCBvbmx5IGtlZXAgdGhlIGRhdGUsIHRoZSBwYWlyLCBhbmQgdGhlIHByaWNlCmFsdF9kYXRhX3N1Yl9wY3QgPC0gYWx0X2RhdGFbLCAuKERhdGUsIHBhaXJfdXNkdCwgcGN0X2NoYW5nZSldCgojIGNvbnZlcnQgdG8gd2lkZSBmb3JtYXQgCmFsdF9kYXRhX3N1Yl9wY3QgPC0gc3ByZWFkKGRhdGEgPSBhbHRfZGF0YV9zdWJfcGN0LCBrZXkgPSAicGFpcl91c2R0IiwgdmFsdWUgPSAicGN0X2NoYW5nZSIpCgojIGNsZWFuIGNvbHVtbiBuYW1lcwpzZXRuYW1lcyhhbHRfZGF0YV9zdWJfcGN0LCBnc3ViKCJVU0RUXyIsICIiLCBjb2xuYW1lcyhhbHRfZGF0YV9zdWIpKSkKCiMgc3Vic2V0IHRoZSBkYXRhCmJ0Y19sdGMgPC0gYWx0X2RhdGFfc3ViX3BjdFssIC4oRGF0ZSwgQlRDLCBMVEMpXQoKIyBhZGQgYSB5ZWFyX21vbnRoIGNvbHVtbgpidGNfbHRjWywgeWVhcl9tb250aCA6PSBhcy55ZWFybW9uKERhdGUpXQoKIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG9uIG1vbnRseSBiYXNpcwpidGNfbHRjXzIgPC0gYnRjX2Rhc2hbLCBjb3IoQlRDLCBMVEMpLCBieSA9IHllYXJfbW9udGhdCgojIG5vdyBwbG90IHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBhcyBhIGZ1bmN0aW9uIG9mIG1vbnRoIGFuZCB5ZWFyCnBsb3QoYnRjX2x0Y18yJHllYXJfbW9udGgsIGJ0Y19sdGNfMiRWMSwgeGxhYiA9ICJZZWFyLU1vbnRoIiwgbWFpbiA9ICJDb3JyZWxhdGlvbiBDb2VmZi4gQmV0d2VlbiBCVEMgYW5kIExUQyBPdmVyIHRpbWUiCiAgICAgLCB5bGFiID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IiwgdHlwZSA9ICJiIiwgcGNoID0gMTksIGNvbCA9IGlmZWxzZShidGNfbHRjXzIkVjEgPiAwLCAiYmx1ZSIsICJyZWQiKQogICAgICwgeWxpbSA9IGMoLTEsIDEpKQoKYGBgCgoKIyMgUm9sbGluZyBDb3JyZWxhdGlvbnMKIAoKCg==